package com.work.order.aop;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

@Aspect
@Component
public class IdempotentAspect {

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Around("@annotation(idempotent)")
    public Object handleIdempotent(ProceedingJoinPoint joinPoint, Idempotent idempotent) throws Throwable {
        // 获取用户标识，这里假设用户标识为 String 类型，您可以根据实际情况修改
        String userId = getUserId();

        // 构建锁的 key 和用户提交时间戳的 key
        String lockKey = "idempotent:" + userId;
        String timestampKey = "idempotent:timestamp:" + userId;

        // 尝试获取锁，设置超时时间，避免死锁
        boolean isLocked = redisTemplate.opsForValue().setIfAbsent(lockKey, "locked", 5, TimeUnit.SECONDS);

        try {
            if (isLocked) {
                // 获取用户上次提交的时间戳
                String lastTimestamp = redisTemplate.opsForValue().get(timestampKey);

                // 检查时间戳，如果在一段时间内已经提交过，则拒绝请求
                //该用户只能10秒内提交一次
                if (lastTimestamp != null && System.currentTimeMillis() - Long.parseLong(lastTimestamp) < TimeUnit.SECONDS.toMillis(10)) {
                    throw new RuntimeException("一段时间内只能提交一次");
                }

                // 执行目标方法
                Object result = joinPoint.proceed();

                // 记录用户提交时间戳
                redisTemplate.opsForValue().set(timestampKey, String.valueOf(System.currentTimeMillis()), 1, TimeUnit.MINUTES);

                return result;
            } else {
                // 如果未获取到锁，说明已经有其他请求在处理，可以进行重试或返回提示信息
                throw new RuntimeException("请勿频繁提交");
            }
        } finally {
            // 释放锁
            if (isLocked) {
                redisTemplate.delete(lockKey);
            }
        }
    }

    // 获取用户标识的示例方法，您需要根据实际情况实现
    private String getUserId() {
        // TODO: 实现获取用户标识的逻辑
        return "user123";
    }
}
